package lol.clann.bukkit.pluginboot.model.rawmessage;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lol.clann.bukkit.pluginboot.util.PlayerUtils;
import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.context.BeanContext;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author pyz
 * @date 2018/8/18 上午10:09
 */
public class RawMessage {

    private final List<MessagePart> parts = new ArrayList<>();
    @Inject
    PlayerUtils playerUtils;
    @Inject
    RawMessaeUtils rawMessaeUtils;

    /**
     * Creates a new {@link RawMessage} object
     *
     * @param text The text to start with
     */
    RawMessage(String text) {
        BeanContext.inject(this);
        parts.add(new MessagePart(text));
    }

    /**
     * Creates a new {@link RawMessage} object
     *
     * @param text The text to start with
     * @return A new {@link RawMessage} object
     */
    public static RawMessage createRawMessage(String text) {
        return new RawMessage(text);
    }

    /**
     * Creates a new {@link RawMessage} object
     *
     * @return A new {@link RawMessage} object
     */
    public static RawMessage createRawMessage() {
        return createRawMessage("");
    }

    /**
     * @return The latest {@link MessagePart}
     * @throws ArrayIndexOutOfBoundsException If {@code parts.size() <= 0}.
     */
    public MessagePart last() {
        if (parts.size() <= 0) {
            throw new ArrayIndexOutOfBoundsException("No MessageParts exist!");
        }
        return parts.get(parts.size() - 1);
    }

    /**
     * Converts this {@link RawMessage} instance to actual JSON
     *
     * @return The JSON representation of this {@link RawMessage}
     */
    public ObjectNode toJSON() {
        ObjectNode obj = JsonNodeFactory.instance.objectNode();
        obj.put("text", "");
        ArrayNode array = JsonNodeFactory.instance.arrayNode();
        parts.stream()
                .map(MessagePart::toJSON)
                .forEach(array::add);

        obj.put("extra", array);
        return obj;
    }

    /**
     * Converts this {@link RawMessage} object to a String representation of the JSON.
     * This is an alias of {@code toJSON().toString()}.
     */
    @Override
    public String toString() {
        return toJSON().toString();
    }

    /**
     * Converts this {@link RawMessage} object to the legacy formatting system, which
     * uses formatting codes (like &amp;6, &amp;l, &amp;4, etc.)
     *
     * @return This {@link RawMessage} instance {@link RawMessage} in legacy format
     */
    public String toLegacy() {
        StringBuilder output = new StringBuilder();

        parts.stream()
                .map(MessagePart::toLegacy)
                .forEach(output::append);

        return output.toString();
    }

    /**
     * Sends this {@link RawMessage} to all the players specified
     *
     * @param senders The players you want to send this to
     */
    public void send(CommandSender... senders) {
        Object packet = rawMessaeUtils.createTextPacket(toString());
        String normalMessage = null;
        for (CommandSender sender : senders) {
            if (sender instanceof Player) {
                playerUtils.sendPacket((Player) sender, packet);
            } else {
                if (normalMessage == null) {
                    normalMessage = this.toLegacy();
                }
                sender.sendMessage(normalMessage);
            }
        }
    }

    public void send(Collection<CommandSender> senders) {
        Object packet = rawMessaeUtils.createTextPacket(toString());
        String normalMessage = null;
        for (CommandSender sender : senders) {
            if (sender instanceof Player) {
                playerUtils.sendPacket((Player) sender, packet);
            } else {
                if (normalMessage == null) {
                    normalMessage = this.toLegacy();
                }
                sender.sendMessage(normalMessage);
            }
        }
    }

    public void boardcast() {// boardcast
        Player[] players = Bukkit.getOnlinePlayers().toArray(new Player[0]);
        send(players);
        send(Bukkit.getConsoleSender());
    }

    /**
     * Sets the color of the current message part.
     *
     * @param color The color to set
     * @return This {@link RawMessage} instance
     */
    public RawMessage color(ChatColor color) {
        last().setColor(color);
        return this;
    }

    /**
     * Adds a style to the current message part.
     *
     * @param style The style to add
     * @return This {@link RawMessage} instance
     */
    public RawMessage style(ChatColor style) {
        last().addStyle(style);
        return this;
    }

    /**
     * Makes the text run a command.
     *
     * @param command The command to run
     * @return This {@link RawMessage} instance
     */
    public RawMessage runCommand(String command) {
        last().setOnClick(ClickEvent.runCommand(command));
        return this;
    }

    /**
     * Makes the text suggest a command.
     *
     * @param command The command to suggest
     * @return This {@link RawMessage} instance
     */
    public RawMessage suggestCommand(String command) {
        last().setOnClick(ClickEvent.suggestCommand(command));
        return this;
    }

    /**
     * Opens a URL.
     *
     * @param url The url to open
     * @return This {@link RawMessage} instance
     */
    public RawMessage openURL(String url) {
        last().setOnClick(ClickEvent.openURL(url));
        return this;
    }

    /**
     * Shows text when you hover over it
     *
     * @param text The text to show
     * @return This {@link RawMessage} instance
     */
    public RawMessage tooltip(String text) {
        last().setOnHover(HoverEvent.showText(text));
        return this;
    }


    /**
     * Shows text when you hover over it
     *
     * @param message The text to show
     * @return This {@link RawMessage} instance
     */
    public RawMessage tooltip(RawMessage message) {
        last().setOnHover(HoverEvent.showText(message));
        return this;
    }

    public RawMessage showItem(ItemStack itemStack) {
        last().setOnHover(HoverEvent.showItemStack(itemStack));
        return this;
    }

    /**
     * Shows an achievement when you hover over it
     *
     * @param id The id of the achievement
     * @return This {@link RawMessage} instance
     */
    public RawMessage achievement(String id) {
        last().setOnHover(HoverEvent.showAchievement(id));
        return this;
    }

    /**
     * Adds another part to this {@link RawMessage}
     *
     * @param text The text to start the next {@link MessagePart} with
     * @return This {@link RawMessage} instance
     */
    public RawMessage then(String text) {
        return then(new MessagePart(text));
    }

    /**
     * Adds another part to this {@link RawMessage}
     *
     * @param nextPart The next {@link MessagePart}
     * @return This {@link RawMessage} instance
     */
    public RawMessage then(MessagePart nextPart) {
        parts.add(nextPart);
        return this;
    }

    /**
     * Adds a blank line to the message
     *
     * @return This {@link RawMessage} instance
     */
    public RawMessage newline() {
        return then("\n");
    }

}
